import json
import zlib
import base64
from datetime import datetime, timedelta
import random
import os
import time
import hmac
import hashlib

import settings

_randrange = random.SystemRandom().randrange
_pid = os.getpid()
MAX_SESSION_KEY = 18446744073709551616L     # 2 << 63

class Session(object):
    def __init__(self, request):
        self.session_key = None
        if settings.SESSION_COOKIE_NAME in request.COOKIES:
            self._session = self._decode(request.COOKIES[settings.SESSION_COOKIE_NAME].value)
            self.session_key = self._session.get('id')
            self._needs_save = False
        if self.session_key is None:
            self.new_session()
        self._accessed = False

    def __getitem__(self, key):
        self._accessed = True
        return self._session[key]

    def __contains__(self, key):
        self._accessed = True
        return key in self._session

    def __setitem__(self, key, value):
        self._accessed = True
        self._needs_save = True
        self._session[key] = value

    def __delitem__(self, key):
        self._accessed = True
        self._needs_save = True
        del self._session[key]

    def get(self,key,default=None):
        try:
            return self.__getitem__(key)
        except KeyError:
            return default

    def has_key(self, key):
        return self.__contains__(key)

    def accessed(self):
        return self._accessed

    def new_session(self):
        self.session_key = self._generate_sessionid()
        self._session = {'id': self.session_key, '_session_expires': time.time() + settings.SESSION_COOKIE_AGE}
        self._needs_save = True

    def expire_at_browser_close(self):
        self._session["_session_expires"] = None
        self._needs_save = True

    def save(self, request, response):
        if not self._needs_save:
            return
        session_data = self._encode(self._session)
        expires_time = self._session["_session_expires"]
        if expires_time:
            max_age = max(int(expires_time - time.time()), 0)
        else:
            max_age = 0
        if hasattr(settings, "SESSION_COOKIE_DOMAIN"):
            domain = settings.SESSION_COOKIE_DOMAIN
        else:
            domain = None
        response.set_cookie(settings.SESSION_COOKIE_NAME, session_data, max_age, domain=domain)

    def _generate_sessionid(self):
        session_key = hmac.new(settings.SECRET_KEY, "%s%s%s" % (_randrange(0, MAX_SESSION_KEY), _pid, time.time()), hashlib.sha1).hexdigest()
        return session_key

    def _encode(self, session_dict):
        encoded = base64.b64encode(zlib.compress(json.dumps(session_dict)))
        signature = hmac.new(settings.SECRET_KEY, encoded, hashlib.sha1).hexdigest()
        return encoded + signature

    def _decode(self, session_data):
        try:
            encoded, signature = session_data[:-40], session_data[-40:]
            if hmac.new(settings.SECRET_KEY, encoded, hashlib.sha1).hexdigest() != signature:                
                return {}
            return json.loads(zlib.decompress(base64.b64decode(encoded)))
        except:
            return {}

class middleware:
    def do_request(self, request):
        request.session = Session(request)
    def do_response(self, request, response):
        if hasattr(request, "session"):
            request.session.save(request, response)
            request.session = None
    def do_exception(self, request, response):
        if hasattr(request, "session"):
            request.session.save(request, response)
            request.session = None
        
